
package SSF.Net;

import java.util.*;

import SSF.OS.*;
import com.renesys.raceway.DML.*;
import SSF.Net.Util.*;
import com.renesys.raceway.SSF.*;

/** Network Host: a machine with one or more network interface cards (NICs)
  * through which it interoperates with the world.  A host is a 
  * ProtocolGraph, which means 
  * that it runs a complete network protocol stack.  Minimally, this   
  * stack must include IP and at least one NIC.  
  */
public class Host extends ProtocolGraph {

/** Top-level Net within which this Host appears */
  public Net net;
  
/** Top-level Net within which this Host appears */
  public Net getNet() { return net; };


//----------------------------------------------------- NHI ADDRESSING

/** NHI network context within which this host is defined, relative to
  * which it performs NHI address lookups.  For example, if this host is 
  * defined in subnetwork 3 of network 1, nhi_context will equal "1:3".
  */
  public String nhi_context;

/** Host address in NHI format ("N:N:N:N:H" where N and H are integer IDs). 
  * Equal to nhi_context+":"+ID.
  */
  public String nhi;

/** Translate relative NHI address nspec to an IP address.  For example, 
  * to find the IP address of interface 4, host number 1 would call 
  * local_nhi_to_ip("1(4)").  Returns an IP address in a.b.c.d format.
  */
  public String local_nhi_to_ip(String nspec) {
    return global_nhi_to_ip(cidrBlock.nhi_concat(nhi_context,nspec));
  }

/** Translate global NHI address nspec to an IP address.  For example,
  * to find the IP address of interface 4 of host 1 in subnetwork 3 of 
  * network 1, one might call global_nhi_to_ip("1:3:1(4)").  Returns
  * an IP address in a.b.c.d format.
  */
  public String global_nhi_to_ip(String nspec) {
    return net.cidrMap.nhi_to_ip(nspec);
  }

/** Return the CIDR block descriptor for the network where this host 
  * was defined.  
  */
  public cidrBlock defined_in_network() {
    return net.cidrMap.nhi(nhi);
  }

/** Host identifier for the sole purpose of establishing config file 
  * mappings. 
  */
  public int ID;

//----------------------------------------------------- INTERFACES

/**
  * Number of interfaces constructed for this Host (may not all be "up")
  */
  int interfaceCount;

/** Hashtable mapping IP addresses (Integers) to network interface cards 
  * (NICs).  For example, to get the local network interface corresponding
  * to IP address "128.36.13.1", one might call <br>
  * <pre>
  *      interfaceAddresses.get(new Integer(IP_s.StrToInt("128.36.13.1")));
  * </pre>
  * The 
  */
  public Hashtable interfaceAddresses;

/** Hashtable mapping interface indices (Integers) to network interface cards
  * (NICs).  For example, to find local network interface number 4, 
  * one might call <br>
  * <pre>
  *      interfaceNumbers.get(new Integer(4));
  */
  public Hashtable interfaceNumbers;


//------------------------------------------------- CONSTRUCTOR, TOSTRING

/** Host constructor, called by Net.config().  
@arg N network within which the host is to be configured
@arg use_nhi NHI address of the containing network
@arg use_id integer host ID 
*/
  public Host(Net N, String use_nhi, int use_id) {
    interfaceAddresses = new Hashtable();
    interfaceNumbers = new Hashtable();
    nhi_context = use_nhi;
    nhi = cidrBlock.nhi_concat(use_nhi,use_id);
    ID = use_id;
    net = N;
  }

  public String toString() {
    String ret = "{Host "+nhi+" {";
    for (Enumeration inum = interfaceNumbers.elements();
	 inum.hasMoreElements();) 
      ret = ret + ":"+(NIC)inum.nextElement();
    return (ret+"}}");
  }

//------------------------------------------------- CONFIGURATION

/** Cached reference to the configuration that was used to build this host. */
  public Configuration hostConfig;

/**
  * Configuration of a network host. <p>
  *
  *  REQUIRED ATTRIBUTES:<br>
  *  <ul><li><b>graph</b> --- ProtocolGraph configuration (see ProtocolGraph)
  *  </ul><p>
  *
  *  OPTIONAL ATTRIBUTES:<br> 
  *  <ul><li><b>interface</b> --- NIC configuration  (see NIC)
  *      <li><b>route</b> --- routing table entry (see RadixTreeRoutingTable)
  *      <li><b>alignment</b> --- timeline identifier (string or int)
  *  </ul><p>
  *
  *  The <b>timeline</b> attribute symbolically identifies a timeline to 
  *  which the Host should be aligned.  If none is specified, the timeline
  *  of the containing Net is used, if any.  SSF will attempt to assign
  *  one processor to each timeline so specified, plus one processor 
  *  for unspecified alignments, up to the system-dependent maximum. <p>
  *
  */
  public void config(Configuration cfg) throws configException {
      
    hostConfig = cfg;

    // 1. Get the optional timeline (partition) alignment for this Host..
    Net.configTimeline(this,cfg); 

    try {
      Configuration gcfg;

      Object graph  = cfg.findSingle("graph");

      // 2. Configure the protocol graph for this host...

      if (graph == null) 	
	throw new configException("Host must have graph attribute: "+cfg);
      else if (graph instanceof String) {
	throw new configException("Graph attribute with filename value has been deprecated; please use _find instead");
      } else 
	gcfg = (Configuration)graph;

      super.config(gcfg);

      ProtocolSession IP = SessionForName ("ip");

      // 3. Finally, configure the specified interface set for this host.

      for (Enumeration ifaces = cfg.find("interface");
	   ifaces.hasMoreElements();) {
	Configuration ncfg = (Configuration)ifaces.nextElement();
	idrange ids = new idrange(); ids.config(ncfg);
	for (int id = ids.minid; id<=ids.maxid; id++){

	  NIC nic = new NIC(this,id);
	  nic.config(ncfg);

          if (nic.isVirtual) {
            // Virtual interfaces don't get added to IP by their link layers
            // (since they don't have actual link layers), so we need to
            // manually call IP.opened() here to add them.
            IP.opened(nic);
          }

	  String ipspec = global_nhi_to_ip(nhi + "(" + nic.ID + ")");
	  if (ipspec == null) {
            if (!nic.isVirtual) {
              System.err.println("Warning: interface "+nhi+"("+nic.ID+
                                 ") is unconfigured and has no "+
                                 "assigned IP address");
            }
	  } else { 
	    nic.ipAddr = IP_s.StrToInt(ipspec);
	    nic.maskBits = IP_s.getMaskBits(ipspec);
	    interfaceAddresses.put(new Integer(nic.ipAddr),nic);
	    interfaceNumbers.put(new Integer(nic.ID),nic);
	    interfaceCount++;
	  }
	}
      }
    } catch (ProtocolException pe) {
      throw new configException (pe.toString());
    }
  }

    /**
     * init the NICs besides the protocolSessions.
     */
    public void init()
    {
	//init the protocolSessions.
	super.init();
	
	//init the NICs
	for (Iterator iter = interfaceNumbers.values().iterator(); 
	     iter.hasNext();) {
	    try {
		((NIC)(iter.next())).init();
	    } catch (ProtocolException pex) {
		pex.printStackTrace();
		System.exit(-1);
	    }
	}
    }
}


/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
